package sinica.googlefileservice.server.datastore;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipInputStream;

import javax.jdo.JDOObjectNotFoundException;
import javax.jdo.PersistenceManager;
import javax.jdo.Transaction;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

import org.apache.commons.io.output.ByteArrayOutputStream;
import com.google.appengine.api.datastore.Blob;
import sinica.googlefileservice.server.datastore.util.PMF;

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class IAFile {
	public final static int ENTITY_SIZE = 1024000; //1MB
	
	@PrimaryKey
	@Persistent
	private String id;
	@Persistent
	private List<GoogleUnit> data;
	public IAFile(String id, List<GoogleUnit> data)
	{
		this.id = id;
		this.data = data;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getId() {
		return id;
	}
	public void setData(List<GoogleUnit> data) {
		this.data = data;
	}
	public List<GoogleUnit> getData() {
		return data;
	}
	public static int save(IAFile file)
	{
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Transaction tx = pm.currentTransaction();
		try {
			tx.begin();
			pm.makePersistent(file);
			tx.commit();
		} finally {
			if (tx.isActive()) {
				tx.rollback();
			}
			pm.close();
		}
		return 0;
	}
	public static int delete(IAFile file)
	{
		int  rt = -1;
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			pm.deletePersistent(file);
			rt = 0;
		} catch (JDOObjectNotFoundException e) {
		} finally {
			pm.close();
		}
		return rt;
	}
	public static int delete(String id)
	{
		int rt = -1;
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			IAFile file = pm.getObjectById(IAFile.class, id);
			pm.deletePersistent(file);
			rt = 0;
		} catch (JDOObjectNotFoundException e) {
    		//no entities in Google datastore
		} finally {
			pm.close();
		}
		return rt;
	}
	
	public static IAFile load(String id)
	{
		IAFile file = null;
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			file = pm.getObjectById(IAFile.class, id);
			for (GoogleUnit gu: file.getData()) {
				gu.getData(); //trick
			}
			file.setData(file.getData()); //trick
		} catch (JDOObjectNotFoundException e) {
		} finally {
			pm.close();
		}
	
		return file;
	}
	public static List<GoogleUnit> toGoogleUnits(InputStream input) throws IOException {
		List<GoogleUnit> list = new ArrayList<GoogleUnit>();
		ByteArrayOutputStream output = new ByteArrayOutputStream();
		byte[] buffer = new byte[4096];
		int pBytesRead = 0; //The total number of bytes, which have been read so far. (Range: 0 ~ ENTITY_SIZE)
		long pContentLength = 0; //The total number of bytes, which are being read. (The total file size)
		int n = 0;
		while (-1 != (n = input.read(buffer))) {
			pBytesRead += n;
			pContentLength += n;
			if (pBytesRead >= ENTITY_SIZE) {
				output.write(buffer, 0, n-(pBytesRead-ENTITY_SIZE));
				//put data of ENTITY_SIZE bytes into GoogleUnit
				GoogleUnit gUnit = new GoogleUnit(new Blob(output.toByteArray()));
            	list.add(gUnit);
            	output.reset();
            	if (pBytesRead-ENTITY_SIZE > 0) {
            		output.write(buffer, n-(pBytesRead-ENTITY_SIZE), pBytesRead-ENTITY_SIZE);
            		//recounter pBytesRead
            		pBytesRead = pBytesRead-ENTITY_SIZE;
            	}
			} else {
				output.write(buffer, 0, n);
			}
        }
        if (pContentLength < ENTITY_SIZE || pBytesRead < ENTITY_SIZE) {
        	//put data of less than ENTITY_SIZE bytes into GoogleUnit
        	GoogleUnit gUnit = new GoogleUnit(new Blob(output.toByteArray()));
        	list.add(gUnit);
        }
        output.close();
		return list;
	}
	
	public static List<GoogleUnit> GetEncriptFile(InputStream input) throws IOException {
		
		ByteArrayOutputStream output = new ByteArrayOutputStream();
		byte[] buffer = new byte[4096];
		
		int n = 0;
		while (-1 != (n = input.read(buffer))) {
			output.write(buffer, 0, n);
		}
		ZipInputStream zip = new ZipInputStream(new ByteArrayInputStream(output.toByteArray()));
		output.reset();
		int pBytesRead = 0;
		long pContentLength = 0;
		List<GoogleUnit> list = new ArrayList<GoogleUnit>();
		//ZipEntry zipEntry = null;  
		while ( zip.getNextEntry() != null )  
		{
			while ((n = zip.read(buffer, 0, buffer.length)) != -1){
				pBytesRead += n;
				pContentLength += n;
				if (pBytesRead >= ENTITY_SIZE) {
					output.write(buffer, 0, n-(pBytesRead-ENTITY_SIZE));
					GoogleUnit gUnit = new GoogleUnit(new Blob(output.toByteArray()));
	            	list.add(gUnit);
	            	output.reset();
	            	if (pBytesRead-ENTITY_SIZE > 0) {
	            		output.write(buffer, n-(pBytesRead-ENTITY_SIZE), pBytesRead-ENTITY_SIZE);
	            		pBytesRead = pBytesRead-ENTITY_SIZE;
	            	}
				} else {
					output.write(buffer, 0, n);
				}
	        }
		}
        if (pContentLength < ENTITY_SIZE || pBytesRead < ENTITY_SIZE) {
        	GoogleUnit gUnit = new GoogleUnit(new Blob(output.toByteArray()));
        	list.add(gUnit);
        }
        output.close();
        zip.close();
		return list;
	}
}
